Contents

1 Dimensionality reduction and clustering on scRNA-seq data

We will work on scRNA-seq data of mouse gastrulation and early organogenesis from Pijuan-Sala et al., 2019. This application provides an interactive interface that allows users to validate their own analysis on this data. You can reach the original data and related scripts from the Github page.

Gastrulation is a phase early in the embryonic development of most animals, during which the single-layered blastula is reorganized into a multilayered structure known as the gastrula (Wikipedia, 2020-06-02).

1.1 Getting familiar with the pre-processed data

Let’s start with including required libraries.

  suppressPackageStartupMessages({
  library(Seurat)
  library(ggplot2)
  library(dplyr)
  library(data.table)
  library(cowplot)
  set.seed(1)
})

In the previous practical, we have learned the essential preprocessing steps for working with scRNA-seq. Here we will start working with the preprocessed version of the mouse gastrulation scRNA-seq data in order to save time. We will load the pre-saved Seurat object of this data.

    mgsc <- readRDS('data/gastrulation/mgsc.rds')
    mgsc
## An object of class Seurat 
## 29452 features across 116312 samples within 1 assay 
## Active assay: RNA (29452 features, 0 variable features)
Reminder: number of rows = genes = features, and number of columns = cells = samples
    # let's see the available slots
    slotNames(mgsc)
##  [1] "assays"       "meta.data"    "active.assay" "active.ident" "graphs"      
##  [6] "neighbors"    "reductions"   "project.name" "misc"         "version"     
## [11] "commands"     "tools"

Now let us look at to the metadata table of this data which contains an overview of the samples.

    # see it is empty for now
    head(mgsc@meta.data, 5)
## data frame with 0 columns and 5 rows
    # now let's load the information to a table
    metadata <- fread('data/gastrulation/sample_metadata.txt.gz') %>% .[stripped==FALSE & doublet==FALSE]
    
    # and add  metadata  to our seurat object
    mgsc <- AddMetaData(mgsc, metadata = data.frame(metadata, row.names = metadata$cell))
    
    # now let's see once more
    head(mgsc@meta.data, 5)
##          cell        barcode sample stage sequencing.batch doublet stripped
## cell_1 cell_1 AAAGGCCTCCACAA      1  E6.5                1   FALSE    FALSE
## cell_2 cell_2 AACAAACTCGCCTT      1  E6.5                1   FALSE    FALSE
## cell_5 cell_5 AACAGAGAATCAGC      1  E6.5                1   FALSE    FALSE
## cell_6 cell_6 AACATATGAATCGC      1  E6.5                1   FALSE    FALSE
## cell_8 cell_8 AACCGATGGCTTCC      1  E6.5                1   FALSE    FALSE
##                celltype      umapX      umapY        celltype2    celltype3
## cell_1         Epiblast -10.227546 -2.8816875         Epiblast  Epiblast-PS
## cell_2 Primitive_Streak  -6.625458  0.1089605 Primitive_Streak  Epiblast-PS
## cell_5     ExE_ectoderm  10.061009 -0.0293132     ExE_ectoderm ExE_ectoderm
## cell_6         Epiblast -10.454418 -0.2694517         Epiblast  Epiblast-PS
## cell_8         Epiblast -11.047206 -2.2052687         Epiblast  Epiblast-PS

Now we are able to see the annonations for each cell, e.g. cell types. There is also a column named stage column which shows the embryonic day the cells were sequenced. As our dataset is quite large, we will work on a subset of cells that belong to stage E6.75 in order to speed up our response time.

    mgsc_subset <- mgsc[ , mgsc@meta.data$stage=='E6.75']
    mgsc_subset
## An object of class Seurat 
## 29452 features across 2075 samples within 1 assay 
## Active assay: RNA (29452 features, 0 variable features)
    # now lets save this subset
    saveRDS(mgsc_subset, file = "data/gastrulation/mgsc_e675.rds")
    
    mgsc <- mgsc_subset

This is where you are starting from! :) Now you can go ahead and load mgsc_e675.rds object to start working! Make sure that the object you loaded has same numbers of columns and rows with E6.75 Seurat object.

Q1: Let’s warm up! Can you try to subset cells in locations \(1, 23, 515\) and genes in locations \(21, 44, 116\), respectively?

## An object of class Seurat 
## 29452 features across 3 samples within 1 assay 
## Active assay: RNA (29452 features, 0 variable features)
## An object of class Seurat 
## 3 features across 2075 samples within 1 assay 
## Active assay: RNA (3 features, 0 variable features)

We can use rownames and colnames to see the names of genes and cells.

# rownames = gene names
head(rownames(mgsc))
## [1] "ENSMUSG00000051951" "ENSMUSG00000089699" "ENSMUSG00000102343"
## [4] "ENSMUSG00000025900" "ENSMUSG00000025902" "ENSMUSG00000104328"
# colnames = sample/cell names
head(colnames(mgsc))
## [1] "cell_5456" "cell_5457" "cell_5458" "cell_5459" "cell_5460" "cell_5461"

Q2: Observe the count data of the first 30 cells for genes “ENSMUSG00000051951” and “ENSMUSG00000033845”. (Hint: use GetAssayData function. )

## 2 x 30 sparse Matrix of class "dgCMatrix"
##    [[ suppressing 30 column names 'cell_5456', 'cell_5457', 'cell_5458' ... ]]
##                                                                               
## ENSMUSG00000051951 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
## ENSMUSG00000033845 1 3 8 2 4 2 2 3 5 5 3 6 1 3 2 2 5 3 4 1 2 4 5 2 3 4 6 7 5 6

The . values in the matrix represent \(0\)s. Since most values in an scRNA-seq matrix are \(0\), Seurat uses a sparse-matrix representation to speed up calculations and reduce memory usage.

2 Dimensionality reduction

2.1 Feature selection: identification of highly variable features

Q3: Our feature set (i.e. number of genes) are quite large! What kind of features do you think should be in the dataset?

Seurat implements FindVariableFeatures to model the mean-variance relationship in single-cell data and returns 2000 features per dataset by default. We will use 1000 features to reduce computation time.

mgsc <- FindVariableFeatures(mgsc, selection.method = "vst", nfeatures = 1000)
options(repr.plot.width=12, repr.plot.height=6)

# Identify the 10 most highly variable genes
top10 <- head(VariableFeatures(mgsc), 10)

# plot variable features 
plot1 <- VariableFeaturePlot(mgsc)
LabelPoints(plot = plot1, points = top10, repel = TRUE,  xnudge = 0, ynudge = 0)
## Warning: Transformation introduced infinite values in continuous x-axis

head(HVFInfo(mgsc)[VariableFeatures(mgsc), ], 5)
##                         mean variance variance.standardized
## ENSMUSG00000032083 10.008193 971.0756              24.49617
## ENSMUSG00000024990  3.765301 210.4092              22.38176
## ENSMUSG00000061808  7.784578 546.4217              21.09222
## ENSMUSG00000024503  6.853976 419.2819              19.63029
## ENSMUSG00000024391  3.319036 112.9888              15.76248

2.1.1 Scaling the data

Q4: Seurat implements the ScaleData function to scale the data. Do you remember why we need this step?

mgsc <- ScaleData(mgsc) #Vector of features names to scale/center. Default is variable features.
## Centering and scaling data matrix

The results are stored inmgsc[["RNA"]]@scale.data.

mgsc[["RNA"]]@scale.data[1:5,1:5]
##                     cell_5456  cell_5457  cell_5458  cell_5459  cell_5460
## ENSMUSG00000025902 -0.3250561 -0.3250561 -0.3250561 -0.3250561 -0.3250561
## ENSMUSG00000042182 -0.0428776 -0.0428776 -0.0428776 -0.0428776 -0.0428776
## ENSMUSG00000026124 -0.3533363 -0.3533363 -0.3533363 -0.3533363 -0.3533363
## ENSMUSG00000008136 -0.3580923 -0.3580923 -0.3580923 -0.3580923 -0.3580923
## ENSMUSG00000025993 -0.3715563 -0.3715563 -0.3715563 -0.3715563 -0.3715563

2.2 Principal Component Analysis

We will RunPCA function of the Seurat.

Q5: Pay attention to the features argument. How many features will PCA work on?

mgsc <- RunPCA(mgsc, npcs = 100, features = VariableFeatures(object = mgsc)) 

We can explore the constructed embeddings via mgsc@reductions.

    slotNames(mgsc@reductions[["pca"]])
## [1] "cell.embeddings"            "feature.loadings"          
## [3] "feature.loadings.projected" "assay.used"                
## [5] "global"                     "stdev"                     
## [7] "key"                        "jackstraw"                 
## [9] "misc"
    dim(mgsc@reductions[["pca"]]@cell.embeddings)
## [1] 2075  100
    mgsc@reductions[["pca"]]@cell.embeddings[1:5,1:5]
##                PC_1      PC_2       PC_3      PC_4        PC_5
## cell_5456 -7.525323 -2.044025 -1.7048600 -3.297759 -0.00652879
## cell_5457 -8.266647 -3.636625  0.6928462 -3.041104 -0.18143907
## cell_5458 -7.687170 -3.591576 -2.1696897 -3.157680 -0.03483741
## cell_5459 -7.256699 -2.657866 -1.7883639 -3.313753 -1.10164595
## cell_5460 -7.410198 -2.796104 -1.9718807 -4.048726 -0.79348906
  # (1) we can visualize top genes associated with pca embeddings
  VizDimLoadings(mgsc, dims = 1:2, reduction = "pca")

  VizDimLoadings(mgsc, dims = 3:4, reduction = "pca")

Q6: Generate the following output: three 2D plots with the first six PCs and print them side by side. i.e. PC1-PC2, PC3-PC4, PC5-PC6. Utilize DimPlot function of Seurat.

2.2.1 Determine the ‘dimensionality’ of the dataset

Q7: Name that one method that we used to determine the dimensionality. Find the corresponding Seurat function and use it to print the following plot. How many dimensions do you suggest we keep?

Another alternative is to use JackStraw function. Here is how Seurat defines it:

Randomly permutes a subset of data, and calculates projected PCA scores for these ‘random’ genes. Then compares the PCA scores for the ‘random’ genes with the observed PCA scores to determine statistical signifance. End result is a p-value for each gene’s association with each principal component.

Q8: According to this definition, how do you think we identify important PCs?

    mgsc <- JackStraw(mgsc, dims=50, num.replicate = 100) 
    mgsc <- ScoreJackStraw(mgsc, dims = 1:50)
    JackStrawPlot(mgsc, dims = 1:20)
## Warning: Removed 15496 rows containing missing values (geom_point).

This technique, however, might be time-consuming - especially for larger datasets. Elbow plot is a common practice for its speed.

2.3 Non-linear dimensional reduction (t-SNE/UMAP)

2.3.1 t-SNE

Seurat comprises RunTSNE which uses Rtsne library (which we previously worked with() as a default.

# we are using the PCA embeddings as our input
mgsc <- RunTSNE(mgsc, dims = 1:30,  nthreads = 4, max_iter = 2000)
DimPlot(mgsc, reduction = "tsne")

mgsc@reductions
## $pca
## A dimensional reduction object with key PC_ 
##  Number of dimensions: 100 
##  Projected dimensional reduction calculated:  FALSE 
##  Jackstraw run: TRUE 
##  Computed using assay: RNA 
## 
## $tsne
## A dimensional reduction object with key tSNE_ 
##  Number of dimensions: 2 
##  Projected dimensional reduction calculated:  FALSE 
##  Jackstraw run: FALSE 
##  Computed using assay: RNA

Q9: Do you remember one of the important hyper-parameters of t-SNE? Print t-SNE plots using DimPlot and explore how structure of the printed data changes with this parameter. Keep dimension as \(30\). (Hint: reduction.name will help you save your embeddings that are produced by different algorithms. )

2.3.2 UMAP

mgsc <- RunUMAP(mgsc, dims = 1:30)
DimPlot(mgsc, reduction = "umap")

Q10: How about the important parameters for UMAP? Print following UMAP plots using DimPlot and explore how structure of the printed data changes with these two parameters (you can print it for only one parameter). Choose dimensions between \(1:50\).

Homework: Plot the projections of the dataset with three of the dimensionality reduction techniques printed side by side and explore AugmentPlot.

3 Cluster the cells

For our subset of time point E6.5, we have the cell type annotation information. Although this is not usually the case in many computational problems, i.e. we start without knowing (or having a very vague idea of) how many clusters we will end up. For now let’s take advantage of the known annotations.

Q11: How many \(real\) classes we have? Reproduce the following plots colored by cell type. ( Hint: You can make use group.by argument in DimPlot to extract stored cluster IDs.)

3.1 K-means

Seurat does not support K-means? What are we going to do now!? :)

Q12: Which elements do we need to perform \(K-means\)? Try to reproduce the following plot (\(k=10\)). ( Hint: Make use of Embeddings function or utilize an information we previously used in the previous examples. You can save cluster IDs to mgsc@meta.data field. )

## [1] 2075   30

3.1.1 How many clusters should we choose?

Do you remember our example from the lecture? Which \(k\)-means outcome did we look at the to decide number of clusters?

Q13: Print the following plot using the same embeddings in the previous example. (Hint: Lecture notes:))

Can we decide the number of clusters looking at this plot?

3.2 Hierarchical clustering

Seurat does not support hierarchical clustering too? But we can do it!!

Q14: Which elements do we need to perform hierarchical clustering? Try to reproduce the following dendogram. Use Euclidian as distance metric and ward.D2 as linkage method.

Q15: How about we print different clustering outcomes with \(k=10, 20, 40\) using t-SNE?

3.3 Graph-based clustering

Seuratv3 adopts a graph-based clustering methodology much similar to PhenoGraph approach we discussed earlier.

FindNeighbors function performs the first steps: (1) build a kNN graph using Euclidean distance in PCA space and (2) update the edge weights based on the shared neighbors (Jaccard index) to construct a Shared Nearest Neighbor (SNN) graph.

Once the graph is constucted, we can use FindClusters function to apply a community detection algorithm to identify subgroups. Remember that, Seurat uses the Louvain algorithm as a default for this step.

mgsc <- FindNeighbors(mgsc,  k.param = 20, dims = 1:30, reduction = "pca")
mgsc <- FindClusters(mgsc, resolution = 0.5)
## Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck
## 
## Number of nodes: 2075
## Number of edges: 99889
## 
## Running Louvain algorithm...
## Maximum modularity in 10 random starts: 0.8377
## Number of communities: 4
## Elapsed time: 0 seconds
# Look at cluster IDs of the first 20 cells
head(Idents(mgsc), 20)
## cell_5456 cell_5457 cell_5458 cell_5459 cell_5460 cell_5461 cell_5462 cell_5463 
##         0         0         0         0         0         0         1         0 
## cell_5465 cell_5466 cell_5467 cell_5468 cell_5469 cell_5470 cell_5471 cell_5472 
##         0         1         0         3         0         0         3         3 
## cell_5473 cell_5474 cell_5475 cell_5476 
##         2         3         3         0 
## Levels: 0 1 2 3

FindClusters has a resolution parameter that sets the granularity of the downstream clustering, with increased values leading to a greater number of clusters.

The Seurat authors suggest that granularity values between 0.4-1.2 usually return good results for sc datasets of around 3K cells. For larger datasets, optimal resolution often increases for larger datasets.

Homework: Explore how different values of \(granularity\) affect the clustering results and reproduce the following plot. (Hint: Use Idents function of Seurat to save cluster ids. )

## Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck
## 
## Number of nodes: 2075
## Number of edges: 99889
## 
## Running Louvain algorithm...
## Maximum modularity in 10 random starts: 0.8658
## Number of communities: 4
## Elapsed time: 0 seconds
## Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck
## 
## Number of nodes: 2075
## Number of edges: 99889
## 
## Running Louvain algorithm...
## Maximum modularity in 10 random starts: 0.7710
## Number of communities: 6
## Elapsed time: 0 seconds
## Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck
## 
## Number of nodes: 2075
## Number of edges: 99889
## 
## Running Louvain algorithm...
## Maximum modularity in 10 random starts: 0.7075
## Number of communities: 9
## Elapsed time: 0 seconds

LS0tCnRpdGxlOiAiQ29tcHV0YXRpb25hbCBzaW5nbGUtY2VsbCBiaW9sb2d5IGNvdXJzZSIKYXV0aG9yOiAiSGFraW1lIMOWenTDvHJrIChoLm9lenR1ZXJrQGRrZnotaGVpZGVsYmVyZy5kZSkiCmRhdGU6ICJgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVkICVCLCAlWScpYCIKb3V0cHV0OgogICAgQmlvY1N0eWxlOjpodG1sX2RvY3VtZW50OiAgICAKICAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICAgdG9jOiB5ZXMKCi0tLQoKCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpCmBgYAojIERpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbiBhbmQgY2x1c3RlcmluZyBvbiBzY1JOQS1zZXEgZGF0YQoKV2Ugd2lsbCB3b3JrIG9uIHNjUk5BLXNlcSBkYXRhIG9mIG1vdXNlIGdhc3RydWxhdGlvbiBhbmQgZWFybHkgb3JnYW5vZ2VuZXNpcyBmcm9tIFtQaWp1YW4tU2FsYSBldCBhbC4sIDIwMTldKGh0dHBzOi8vd3d3Lm5hdHVyZS5jb20vYXJ0aWNsZXMvczQxNTg2LTAxOS0wOTMzLTkpLiBbVGhpcyBhcHBsaWNhdGlvbl0oaHR0cHM6Ly9tYXJpb25pbGFiLmNydWsuY2FtLmFjLnVrL01vdXNlR2FzdHJ1bGF0aW9uMjAxOC8pICBwcm92aWRlcyBhbiBpbnRlcmFjdGl2ZSBpbnRlcmZhY2UgdGhhdCBhbGxvd3MgdXNlcnMgdG8gdmFsaWRhdGUgdGhlaXIgb3duIGFuYWx5c2lzIG9uIHRoaXMgZGF0YS4gWW91IGNhbiByZWFjaCB0aGUgb3JpZ2luYWwgZGF0YSBhbmQgcmVsYXRlZCBzY3JpcHRzIGZyb20gdGhlIFtHaXRodWIgcGFnZV0oKS4KCj4gR2FzdHJ1bGF0aW9uIGlzIGEgcGhhc2UgZWFybHkgaW4gdGhlIGVtYnJ5b25pYyBkZXZlbG9wbWVudCBvZiBtb3N0IGFuaW1hbHMsIGR1cmluZyB3aGljaCB0aGUgc2luZ2xlLWxheWVyZWQgYmxhc3R1bGEgaXMgcmVvcmdhbml6ZWQgaW50byBhIG11bHRpbGF5ZXJlZCBzdHJ1Y3R1cmUga25vd24gYXMgdGhlIGdhc3RydWxhIFsoV2lraXBlZGlhLCAyMDIwLTA2LTAyKV0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvR2FzdHJ1bGF0aW9uKS4gCgoKIyMgR2V0dGluZyBmYW1pbGlhciB3aXRoIHRoZSBwcmUtcHJvY2Vzc2VkIGRhdGEKCkxldCdzIHN0YXJ0IHdpdGggaW5jbHVkaW5nIHJlcXVpcmVkIGxpYnJhcmllcy4gCmBgYHtyIGxpYnJhcmllc30KICBzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMoewogIGxpYnJhcnkoU2V1cmF0KQogIGxpYnJhcnkoZ2dwbG90MikKICBsaWJyYXJ5KGRwbHlyKQogIGxpYnJhcnkoZGF0YS50YWJsZSkKICBsaWJyYXJ5KGNvd3Bsb3QpCiAgc2V0LnNlZWQoMSkKfSkKYGBgCgpgYGB7ciBjb252ZXJ0LCBpbmNsdWRlPUZBTFNFfQogICMgaGVyZSBJIGFtIHJlYWRpbmcgYSBTQ0Ugb2JqZWN0IGFuZCBjb252ZXJ0IGl0IHRvIFNldXJhdCBvYmplY3QKICAjbWdzY3NjZSA8LSByZWFkUkRTKCdkYXRhL2dhc3RydWxhdGlvbi9TaW5nbGVDZWxsRXhwZXJpbWVudC5yZHMnKQogICNtZ3NjIDwtIGFzLlNldXJhdChtZ3Njc2NlLCBjb3VudHMgPSAiY291bnRzIiwgZGF0YSA9ICJsb2djb3VudHMiLCBwcm9qZWN0ID0gIm1vdXNlIGdhc3RydWxhdGlvbiIpIAogIAogICMgSSBhbSBzYXZpbmcgdGhpcyBhcyBTZXVyYXQgb2JqZWN0CiAgI3NhdmVSRFMobWdzYywgZmlsZSA9ICJkYXRhL2dhc3RydWxhdGlvbi9tZ3NjLnJkcyIpCgpgYGAKCkluIHRoZSBwcmV2aW91cyBwcmFjdGljYWwsIHdlIGhhdmUgbGVhcm5lZCB0aGUgZXNzZW50aWFsIHByZXByb2Nlc3Npbmcgc3RlcHMgZm9yIHdvcmtpbmcgd2l0aCBzY1JOQS1zZXEuIEhlcmUgd2Ugd2lsbCBzdGFydCB3b3JraW5nIHdpdGggdGhlIHByZXByb2Nlc3NlZCB2ZXJzaW9uIG9mIHRoZSBtb3VzZSBnYXN0cnVsYXRpb24gc2NSTkEtc2VxIGRhdGEgaW4gb3JkZXIgdG8gc2F2ZSB0aW1lLiBXZSB3aWxsIGxvYWQgdGhlIHByZS1zYXZlZCBgU2V1cmF0YCBvYmplY3Qgb2YgdGhpcyBkYXRhLiAKCmBgYHtyIHJlYWR9CiAgICBtZ3NjIDwtIHJlYWRSRFMoJ2RhdGEvZ2FzdHJ1bGF0aW9uL21nc2MucmRzJykKICAgIG1nc2MKYGBgICAgCiAgICBSZW1pbmRlcjogbnVtYmVyIG9mIHJvd3MgPSBnZW5lcyA9IGZlYXR1cmVzLCBhbmQgbnVtYmVyIG9mIGNvbHVtbnMgPSBjZWxscyA9IHNhbXBsZXMKCmBgYHtyIHNsb3R9ICAgIAogICAgIyBsZXQncyBzZWUgdGhlIGF2YWlsYWJsZSBzbG90cwogICAgc2xvdE5hbWVzKG1nc2MpCmBgYAoKTm93IGxldCB1cyBsb29rIGF0IHRvIHRoZSBtZXRhZGF0YSB0YWJsZSBvZiB0aGlzIGRhdGEgd2hpY2ggY29udGFpbnMgYW4gb3ZlcnZpZXcgb2YgdGhlIHNhbXBsZXMuIAoKYGBge3IgbWV0YWRhdGFhZGR9CiAgICAjIHNlZSBpdCBpcyBlbXB0eSBmb3Igbm93CiAgICBoZWFkKG1nc2NAbWV0YS5kYXRhLCA1KQoKICAgICMgbm93IGxldCdzIGxvYWQgdGhlIGluZm9ybWF0aW9uIHRvIGEgdGFibGUKICAgIG1ldGFkYXRhIDwtIGZyZWFkKCdkYXRhL2dhc3RydWxhdGlvbi9zYW1wbGVfbWV0YWRhdGEudHh0Lmd6JykgJT4lIC5bc3RyaXBwZWQ9PUZBTFNFICYgZG91YmxldD09RkFMU0VdCiAgICAKICAgICMgYW5kIGFkZCAgbWV0YWRhdGEgIHRvIG91ciBzZXVyYXQgb2JqZWN0CiAgICBtZ3NjIDwtIEFkZE1ldGFEYXRhKG1nc2MsIG1ldGFkYXRhID0gZGF0YS5mcmFtZShtZXRhZGF0YSwgcm93Lm5hbWVzID0gbWV0YWRhdGEkY2VsbCkpCiAgICAKICAgICMgbm93IGxldCdzIHNlZSBvbmNlIG1vcmUKICAgIGhlYWQobWdzY0BtZXRhLmRhdGEsIDUpCiAgICAKYGBgCgpOb3cgd2UgYXJlIGFibGUgdG8gc2VlIHRoZSBhbm5vbmF0aW9ucyBmb3IgZWFjaCBjZWxsLCBlLmcuIGNlbGwgdHlwZXMuIFRoZXJlIGlzIGFsc28gYSBjb2x1bW4gbmFtZWQgYHN0YWdlYCBjb2x1bW4gd2hpY2ggc2hvd3MgdGhlIGVtYnJ5b25pYyBkYXkgdGhlIGNlbGxzIHdlcmUgc2VxdWVuY2VkLiBBcyBvdXIgZGF0YXNldCBpcyBxdWl0ZSBsYXJnZSwgd2Ugd2lsbCB3b3JrIG9uIGEgc3Vic2V0IG9mIGNlbGxzIHRoYXQgYmVsb25nIHRvIHN0YWdlIGBFNi43NWAgaW4gb3JkZXIgdG8gc3BlZWQgdXAgb3VyIHJlc3BvbnNlIHRpbWUuIAoKYGBge3Igc3Vic2V0fQogICAgbWdzY19zdWJzZXQgPC0gbWdzY1sgLCBtZ3NjQG1ldGEuZGF0YSRzdGFnZT09J0U2Ljc1J10KICAgIG1nc2Nfc3Vic2V0CiAgICAjIG5vdyBsZXRzIHNhdmUgdGhpcyBzdWJzZXQKICAgIHNhdmVSRFMobWdzY19zdWJzZXQsIGZpbGUgPSAiZGF0YS9nYXN0cnVsYXRpb24vbWdzY19lNjc1LnJkcyIpCiAgICAKICAgIG1nc2MgPC0gbWdzY19zdWJzZXQKICAKYGBgCgoKVGhpcyBpcyB3aGVyZSB5b3UgYXJlIHN0YXJ0aW5nIGZyb20hIDopIE5vdyB5b3UgY2FuIGdvIGFoZWFkIGFuZCBsb2FkIGBtZ3NjX2U2NzUucmRzYCAgb2JqZWN0IHRvIHN0YXJ0IHdvcmtpbmchIE1ha2Ugc3VyZSB0aGF0IHRoZSBvYmplY3QgeW91IGxvYWRlZCBoYXMgc2FtZSBudW1iZXJzIG9mIGNvbHVtbnMgYW5kIHJvd3Mgd2l0aCBgRTYuNzVgIFNldXJhdCBvYmplY3QuCgoqKlExOioqIExldCdzIHdhcm0gdXAhIENhbiB5b3UgdHJ5IHRvIHN1YnNldCBjZWxscyBpbiBsb2NhdGlvbnMgICQxLCAyMywgNTE1JCBhbmQgZ2VuZXMgaW4gbG9jYXRpb25zICQyMSwgNDQsIDExNiQsIHJlc3BlY3RpdmVseT8KCgpgYGB7ciBwMSwgZWNobz1GQUxTRX0KICAgIHMxIDwtIG1nc2NbLCBjKDEsMjMsNTE1KV0gIyBTdWJzZXQgdG8gY2VsbHMgMSwgMjMsIDUxNQogICAgczIgPC0gbWdzY1tjKDIxLDQ0LDExNiksIF0gIyBTdWJzZXQgdG8gZ2VuZXMgMjEsIDQ0LCAxMTYgCiAgICAKICAgIHMxCiAgICBzMgpgYGAKCldlIGNhbiB1c2UgYHJvd25hbWVzYCBhbmQgYGNvbG5hbWVzYCB0byBzZWUgdGhlIG5hbWVzIG9mIGdlbmVzIGFuZCBjZWxscy4gCgpgYGB7ciBwMn0KIyByb3duYW1lcyA9IGdlbmUgbmFtZXMKaGVhZChyb3duYW1lcyhtZ3NjKSkKCiMgY29sbmFtZXMgPSBzYW1wbGUvY2VsbCBuYW1lcwpoZWFkKGNvbG5hbWVzKG1nc2MpKQpgYGAKCioqUTI6KiogT2JzZXJ2ZSB0aGUgY291bnQgZGF0YSBvZiB0aGUgZmlyc3QgMzAgY2VsbHMgZm9yIGdlbmVzICJFTlNNVVNHMDAwMDAwNTE5NTEiIGFuZCAiRU5TTVVTRzAwMDAwMDMzODQ1Ii4gKGAgSGludDpgIHVzZSBgR2V0QXNzYXlEYXRhYCBmdW5jdGlvbi4gKQoKYGBge3IgcDMsIGVjaG89RkFMU0V9CkdldEFzc2F5RGF0YShvYmplY3QgPSBtZ3NjLCBzbG90ID0gJ2NvdW50cycpW2MoIkVOU01VU0cwMDAwMDA1MTk1MSIsICJFTlNNVVNHMDAwMDAwMzM4NDUiKSwgMTozMF0KYGBgCgpUaGUgLiB2YWx1ZXMgaW4gdGhlIG1hdHJpeCByZXByZXNlbnQgJDAkcy4gU2luY2UgbW9zdCB2YWx1ZXMgaW4gYW4gc2NSTkEtc2VxIG1hdHJpeCBhcmUgJDAkLCBTZXVyYXQgdXNlcyBhIHNwYXJzZS1tYXRyaXggcmVwcmVzZW50YXRpb24gdG8gc3BlZWQgdXAgY2FsY3VsYXRpb25zIGFuZCByZWR1Y2UgbWVtb3J5IHVzYWdlLgoKIyAgRGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uCgojIyBGZWF0dXJlIHNlbGVjdGlvbjogaWRlbnRpZmljYXRpb24gb2YgaGlnaGx5IHZhcmlhYmxlIGZlYXR1cmVzCgoqKlEzOioqIE91ciBmZWF0dXJlIHNldCAoaS5lLiBudW1iZXIgb2YgZ2VuZXMpIGFyZSBxdWl0ZSBsYXJnZSEgV2hhdCBraW5kIG9mIGZlYXR1cmVzIGRvIHlvdSB0aGluayBzaG91bGQgYmUgaW4gdGhlIGRhdGFzZXQ/Cgo8IS0tIFdlIHdpbGwgY2hvb3NlIGEgc3Vic2V0IG9mIGZlYXR1cmVzIHRoYXQgaGF2ZSBoaWdoIGNlbGwtdG8tY2VsbCB2YXJpYXRpb24gaW4gdGhlIGRhdGFzZXQgKGkuZSwgZ2VuZXMgdGhhdCBhcmUgaGlnaGx5IGV4cHJlc3NlZCBpbiBzb21lIGNlbGxzLCBhbmQgbG93bHkgZXhwcmVzc2VkIGluIG90aGVycykgd2hpY2ggY2FuIGhlbHAgaGlnaGxpZ2h0aW5nIGJpb2xvZ2ljYWwgc2lnbmFsLiAtLT4KClNldXJhdCBpbXBsZW1lbnRzIGBGaW5kVmFyaWFibGVGZWF0dXJlc2AgdG8gbW9kZWwgdGhlIG1lYW4tdmFyaWFuY2UgcmVsYXRpb25zaGlwIGluIHNpbmdsZS1jZWxsIGRhdGEgYW5kIHJldHVybnMgMjAwMCBmZWF0dXJlcyBwZXIgZGF0YXNldCBieSBkZWZhdWx0LiBXZSB3aWxsIHVzZSAxMDAwIGZlYXR1cmVzIHRvIHJlZHVjZSBjb21wdXRhdGlvbiB0aW1lLiAKCmBgYHtyIGZpbmR2YXJpYWJsZX0KbWdzYyA8LSBGaW5kVmFyaWFibGVGZWF0dXJlcyhtZ3NjLCBzZWxlY3Rpb24ubWV0aG9kID0gInZzdCIsIG5mZWF0dXJlcyA9IDEwMDApCm9wdGlvbnMocmVwci5wbG90LndpZHRoPTEyLCByZXByLnBsb3QuaGVpZ2h0PTYpCgojIElkZW50aWZ5IHRoZSAxMCBtb3N0IGhpZ2hseSB2YXJpYWJsZSBnZW5lcwp0b3AxMCA8LSBoZWFkKFZhcmlhYmxlRmVhdHVyZXMobWdzYyksIDEwKQoKIyBwbG90IHZhcmlhYmxlIGZlYXR1cmVzIApwbG90MSA8LSBWYXJpYWJsZUZlYXR1cmVQbG90KG1nc2MpCkxhYmVsUG9pbnRzKHBsb3QgPSBwbG90MSwgcG9pbnRzID0gdG9wMTAsIHJlcGVsID0gVFJVRSwgIHhudWRnZSA9IDAsIHludWRnZSA9IDApCgpoZWFkKEhWRkluZm8obWdzYylbVmFyaWFibGVGZWF0dXJlcyhtZ3NjKSwgXSwgNSkKYGBgCgojIyMgU2NhbGluZyB0aGUgZGF0YSAKCioqUTQ6KiogU2V1cmF0IGltcGxlbWVudHMgdGhlIGBTY2FsZURhdGFgIGZ1bmN0aW9uIHRvIHNjYWxlIHRoZSBkYXRhLiAgRG8geW91IHJlbWVtYmVyIHdoeSAgd2UgbmVlZCB0aGlzIHN0ZXA/CgpgYGB7ciBzY2FsZX0KbWdzYyA8LSBTY2FsZURhdGEobWdzYykgI1ZlY3RvciBvZiBmZWF0dXJlcyBuYW1lcyB0byBzY2FsZS9jZW50ZXIuIERlZmF1bHQgaXMgdmFyaWFibGUgZmVhdHVyZXMuCmBgYAoKVGhlIHJlc3VsdHMgYXJlIHN0b3JlZCBpbmAgbWdzY1tbIlJOQSJdXUBzY2FsZS5kYXRhYC4KCmBgYHtyIHNjYWxlaGVhZH0gCm1nc2NbWyJSTkEiXV1Ac2NhbGUuZGF0YVsxOjUsMTo1XQoKYGBgCgoKCgojIyBQcmluY2lwYWwgQ29tcG9uZW50IEFuYWx5c2lzCgpXZSB3aWxsIGBSdW5QQ0FgIGZ1bmN0aW9uIG9mIHRoZSBTZXVyYXQuIAoKKipRNToqKiBQYXkgYXR0ZW50aW9uIHRvIHRoZSBgZmVhdHVyZXNgIGFyZ3VtZW50LiBIb3cgbWFueSBmZWF0dXJlcyB3aWxsIFBDQSB3b3JrIG9uPwoKYGBge3IgcGNhLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQptZ3NjIDwtIFJ1blBDQShtZ3NjLCBucGNzID0gMTAwLCBmZWF0dXJlcyA9IFZhcmlhYmxlRmVhdHVyZXMob2JqZWN0ID0gbWdzYykpIApgYGAKCldlIGNhbiBleHBsb3JlIHRoZSBjb25zdHJ1Y3RlZCBlbWJlZGRpbmdzIHZpYSAgYG1nc2NAcmVkdWN0aW9uc2AuCgpgYGB7ciByZWR1Y3Rpb25zfQogICAgc2xvdE5hbWVzKG1nc2NAcmVkdWN0aW9uc1tbInBjYSJdXSkKICAgIGRpbShtZ3NjQHJlZHVjdGlvbnNbWyJwY2EiXV1AY2VsbC5lbWJlZGRpbmdzKQogICAgbWdzY0ByZWR1Y3Rpb25zW1sicGNhIl1dQGNlbGwuZW1iZWRkaW5nc1sxOjUsMTo1XQogICAgCmBgYAoKYGBge3IgcGNhdml6LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQoKICAjICgxKSB3ZSBjYW4gdmlzdWFsaXplIHRvcCBnZW5lcyBhc3NvY2lhdGVkIHdpdGggcGNhIGVtYmVkZGluZ3MKICBWaXpEaW1Mb2FkaW5ncyhtZ3NjLCBkaW1zID0gMToyLCByZWR1Y3Rpb24gPSAicGNhIikKICBWaXpEaW1Mb2FkaW5ncyhtZ3NjLCBkaW1zID0gMzo0LCByZWR1Y3Rpb24gPSAicGNhIikKYGBgCgoqKlE2OioqIEdlbmVyYXRlIHRoZSBmb2xsb3dpbmcgb3V0cHV0OiB0aHJlZSAyRCBwbG90cyB3aXRoIHRoZSBmaXJzdCBzaXggUENzIGFuZCBwcmludCB0aGVtIHNpZGUgYnkgc2lkZS4gaS5lLiBQQzEtUEMyLCBQQzMtUEM0LCBQQzUtUEM2LiBVdGlsaXplIGBEaW1QbG90YCBmdW5jdGlvbiBvZiBTZXVyYXQuIAoKYGBge3IgcGNhcHJpbnQsIGVjaG89RkFMU0V9CiAgIyAoMikgCiAgcGxvdF9ncmlkKG5jb2wgPSAzLAogICAgRGltUGxvdChtZ3NjLCByZWR1Y3Rpb24gPSAicGNhIiwgZGltcyA9IDE6MikgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSwKICAgIERpbVBsb3QobWdzYywgcmVkdWN0aW9uID0gInBjYSIsIGRpbXMgPSAzOjQpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksCiAgICBEaW1QbG90KG1nc2MsIHJlZHVjdGlvbiA9ICJwY2EiLCBkaW1zID0gNTo2KSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICkKYGBgCgoKIyMjIERldGVybWluZSB0aGUg4oCYZGltZW5zaW9uYWxpdHnigJkgb2YgdGhlIGRhdGFzZXQKCioqUTc6KiogTmFtZSB0aGF0IG9uZSBtZXRob2QgdGhhdCB3ZSB1c2VkIHRvIGRldGVybWluZSB0aGUgZGltZW5zaW9uYWxpdHkuIEZpbmQgdGhlIGNvcnJlc3BvbmRpbmcgU2V1cmF0IGZ1bmN0aW9uIGFuZCB1c2UgaXQgdG8gcHJpbnQgdGhlIGZvbGxvd2luZyBwbG90LiBIb3cgbWFueSBkaW1lbnNpb25zIGRvIHlvdSBzdWdnZXN0IHdlIGtlZXA/CgpgYGB7ciBlbGJvdywgZWNobz1GQUxTRX0KRWxib3dQbG90KG1nc2MsICBuZGltcyA9IDgwKQpgYGAKCkFub3RoZXIgYWx0ZXJuYXRpdmUgaXMgdG8gdXNlIGBKYWNrU3RyYXdgIGZ1bmN0aW9uLiBIZXJlIGlzIGhvdyBTZXVyYXQgZGVmaW5lcyBpdDogCgo+IFJhbmRvbWx5IHBlcm11dGVzIGEgc3Vic2V0IG9mIGRhdGEsIGFuZCBjYWxjdWxhdGVzIHByb2plY3RlZCBQQ0Egc2NvcmVzIGZvciB0aGVzZSAncmFuZG9tJyBnZW5lcy4gVGhlbiBjb21wYXJlcyB0aGUgUENBIHNjb3JlcyBmb3IgdGhlICdyYW5kb20nIGdlbmVzIHdpdGggdGhlIG9ic2VydmVkIFBDQSBzY29yZXMgdG8gZGV0ZXJtaW5lIHN0YXRpc3RpY2FsIHNpZ25pZmFuY2UuIEVuZCByZXN1bHQgaXMgYSBwLXZhbHVlIGZvciBlYWNoIGdlbmUncyBhc3NvY2lhdGlvbiB3aXRoIGVhY2ggcHJpbmNpcGFsIGNvbXBvbmVudC4KCioqUTg6KiogQWNjb3JkaW5nIHRvIHRoaXMgZGVmaW5pdGlvbiwgaG93IGRvIHlvdSB0aGluayB3ZSBpZGVudGlmeSBpbXBvcnRhbnQgUENzPyAKCjwhLS1UaGUgSmFja1N0cmF3UGxvdCBmdW5jdGlvbiBwcm92aWRlcyBhIHZpc3VhbGl6YXRpb24gdG9vbCBmb3IgY29tcGFyaW5nIHRoZSBkaXN0cmlidXRpb24gb2YgcC12YWx1ZXMgZm9yIGVhY2ggUEMgd2l0aCBhIHVuaWZvcm0gZGlzdHJpYnV0aW9uIChkYXNoZWQgbGluZSkuIOKAmFNpZ25pZmljYW504oCZIFBDcyB3aWxsIHNob3cgYSBzdHJvbmcgZW5yaWNobWVudCBvZiBmZWF0dXJlcyB3aXRoIGxvdyBwLXZhbHVlcyAoc29saWQgY3VydmUgYWJvdmUgdGhlIGRhc2hlZCBsaW5lKS4gLS0+CgoKYGBge3IgamFja3N0cmF3fQogICAgbWdzYyA8LSBKYWNrU3RyYXcobWdzYywgZGltcz01MCwgbnVtLnJlcGxpY2F0ZSA9IDEwMCkgCiAgICBtZ3NjIDwtIFNjb3JlSmFja1N0cmF3KG1nc2MsIGRpbXMgPSAxOjUwKQogICAgSmFja1N0cmF3UGxvdChtZ3NjLCBkaW1zID0gMToyMCkKYGBgCgpUaGlzIHRlY2huaXF1ZSwgaG93ZXZlciwgbWlnaHQgYmUgdGltZS1jb25zdW1pbmcgLSBlc3BlY2lhbGx5IGZvciBsYXJnZXIgZGF0YXNldHMuIEVsYm93IHBsb3QgaXMgYSBjb21tb24gcHJhY3RpY2UgZm9yIGl0cyBzcGVlZC4gCgoKIyMgTm9uLWxpbmVhciBkaW1lbnNpb25hbCByZWR1Y3Rpb24gKHQtU05FL1VNQVApCgojIyMgdC1TTkUKClNldXJhdCBjb21wcmlzZXMgYFJ1blRTTkVgIHdoaWNoIHVzZXMgYFJ0c25lYCBsaWJyYXJ5ICh3aGljaCB3ZSBwcmV2aW91c2x5IHdvcmtlZCB3aXRoKCkgYXMgYSBkZWZhdWx0LiAKCmBgYHtyIHRzbmUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiMgd2UgYXJlIHVzaW5nIHRoZSBQQ0EgZW1iZWRkaW5ncyBhcyBvdXIgaW5wdXQKbWdzYyA8LSBSdW5UU05FKG1nc2MsIGRpbXMgPSAxOjMwLCAgbnRocmVhZHMgPSA0LCBtYXhfaXRlciA9IDIwMDApCkRpbVBsb3QobWdzYywgcmVkdWN0aW9uID0gInRzbmUiKQoKbWdzY0ByZWR1Y3Rpb25zCmBgYAoKKipROToqKiBEbyB5b3UgcmVtZW1iZXIgb25lIG9mIHRoZSBpbXBvcnRhbnQgaHlwZXItcGFyYW1ldGVycyBvZiB0LVNORT8gIFByaW50IHQtU05FIHBsb3RzIHVzaW5nIGBEaW1QbG90YCBhbmQgZXhwbG9yZSAgaG93IHN0cnVjdHVyZSBvZiB0aGUgcHJpbnRlZCBkYXRhIGNoYW5nZXMgd2l0aCB0aGlzIHBhcmFtZXRlci4gS2VlcCBkaW1lbnNpb24gYXMgJDMwJC4gKGAgSGludDpgIGByZWR1Y3Rpb24ubmFtZWAgd2lsbCBoZWxwIHlvdSBzYXZlIHlvdXIgZW1iZWRkaW5ncyB0aGF0IGFyZSBwcm9kdWNlZCBieSBkaWZmZXJlbnQgYWxnb3JpdGhtcy4gKQoKCmBgYHtyIHRzbmVwZXJwLCBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCAgd2FybmluZz1GQUxTRX0KCm1nc2MgPC0gUnVuVFNORShtZ3NjLCBkaW1zID0gMTozMCwgcGVycGxleGl0eT01LCByZWR1Y3Rpb24ubmFtZSA9ICJ0c25lX3A1IiwgbnRocmVhZHMgPSA0LCBtYXhfaXRlciA9IDIwMDApCm1nc2MgPC0gUnVuVFNORShtZ3NjLCBkaW1zID0gMTozMCwgcGVycGxleGl0eT0xNSwgcmVkdWN0aW9uLm5hbWUgPSAidHNuZV9wMTUiLCBudGhyZWFkcyA9IDQsIG1heF9pdGVyID0gMjAwMCkKbWdzYyA8LSBSdW5UU05FKG1nc2MsIGRpbXMgPSAxOjMwLCBwZXJwbGV4aXR5PTMwLCByZWR1Y3Rpb24ubmFtZSA9ICJ0c25lX3AzMF9kZWZhdWx0IiwgbnRocmVhZHMgPSA0LCBtYXhfaXRlciA9IDIwMDApCm1nc2MgPC0gUnVuVFNORShtZ3NjLCBkaW1zID0gMTozMCwgcGVycGxleGl0eT01MCwgcmVkdWN0aW9uLm5hbWUgPSAidHNuZV9wNTAiLCBudGhyZWFkcyA9IDQsIG1heF9pdGVyID0gMjAwMCkKCnBsb3RfZ3JpZChucm93PTIsIG5jb2wgPSAyLAogIERpbVBsb3QobWdzYywgcmVkdWN0aW9uID0gInRzbmVfcDUiKSsgZ2d0aXRsZShsYWJlbCA9InA9NSIpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiksCiAgRGltUGxvdChtZ3NjLCByZWR1Y3Rpb24gPSAidHNuZV9wMTUiKSsgZ2d0aXRsZShsYWJlbCA9InA9MTUiKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLAogIERpbVBsb3QobWdzYywgcmVkdWN0aW9uID0gInRzbmVfcDMwX2RlZmF1bHQiKSsgZ2d0aXRsZShsYWJlbCA9InA9MzAsIGRlZmF1bHQiKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLAogIERpbVBsb3QobWdzYywgcmVkdWN0aW9uID0gInRzbmVfcDUwIikrIGdndGl0bGUobGFiZWwgPSJwPTUwIikgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKQopCgoKYGBgCgoKIyMjIFVNQVAKCmBgYHtyIHVtYXAsICwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbWdzYyA8LSBSdW5VTUFQKG1nc2MsIGRpbXMgPSAxOjMwKQpEaW1QbG90KG1nc2MsIHJlZHVjdGlvbiA9ICJ1bWFwIikKYGBgCgoqKlExMDoqKiBIb3cgYWJvdXQgdGhlIGltcG9ydGFudCBwYXJhbWV0ZXJzIGZvciBVTUFQPyBQcmludCBmb2xsb3dpbmcgVU1BUCBwbG90cyB1c2luZyBgRGltUGxvdGAgYW5kIGV4cGxvcmUgaG93IHN0cnVjdHVyZSBvZiB0aGUgcHJpbnRlZCBkYXRhIGNoYW5nZXMgd2l0aCB0aGVzZSB0d28gcGFyYW1ldGVycyAoeW91IGNhbiBwcmludCBpdCBmb3Igb25seSBvbmUgcGFyYW1ldGVyKS4gQ2hvb3NlIGRpbWVuc2lvbnMgYmV0d2VlbiAkMTo1MCQuCgpgYGB7ciB1bWFwbmVpZ2hib3IsIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CgptZ3NjIDwtIFJ1blVNQVAobWdzYywgcmVkdWN0aW9uLm5hbWUgPSAiVU1BUF9uNSIsIG4ubmVpZ2hib3JzPTUsIGRpbXMgPSAxOjMwKQptZ3NjIDwtIFJ1blVNQVAobWdzYywgcmVkdWN0aW9uLm5hbWUgPSAiVU1BUF9uMjAiLCBuLm5laWdoYm9ycz0yMCwgZGltcyA9IDE6MzApCm1nc2MgPC0gUnVuVU1BUChtZ3NjLCByZWR1Y3Rpb24ubmFtZSA9ICJVTUFQX24zMF9kZWZhdWx0IiwgIGRpbXMgPSAxOjMwKSAKbWdzYyA8LSBSdW5VTUFQKG1nc2MsIHJlZHVjdGlvbi5uYW1lID0gIlVNQVBfbjQwIiwgbi5uZWlnaGJvcnM9NDAsICBkaW1zID0gMTozMCkgCgpwbG90X2dyaWQobnJvdz0yLCBuY29sID0gMiwKICBEaW1QbG90KG1nc2MsIHJlZHVjdGlvbiA9ICJVTUFQX241IikrIGdndGl0bGUobGFiZWwgPSJuPTUiKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpLAogIERpbVBsb3QobWdzYywgcmVkdWN0aW9uID0gIlVNQVBfbjIwIikrIGdndGl0bGUobGFiZWwgPSJuPTIwIikgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSwKICBEaW1QbG90KG1nc2MsIHJlZHVjdGlvbiA9ICJVTUFQX24zMF9kZWZhdWx0IikrIGdndGl0bGUobGFiZWwgPSJuPTMwLCBkZWZhdWx0IikgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSwKICBEaW1QbG90KG1nc2MsIHJlZHVjdGlvbiA9ICJVTUFQX240MCIpKyBnZ3RpdGxlKGxhYmVsID0ibj00MCIpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikKKQoKYGBgCgoKKipIb21ld29yazoqKiBQbG90IHRoZSBwcm9qZWN0aW9ucyBvZiB0aGUgZGF0YXNldCB3aXRoIHRocmVlIG9mIHRoZSBkaW1lbnNpb25hbGl0eSByZWR1Y3Rpb24gdGVjaG5pcXVlcyBwcmludGVkIHNpZGUgYnkgc2lkZSBhbmQgZXhwbG9yZSBgQXVnbWVudFBsb3RgLgoKYGBge3IgY29tcGFyZSwgZWNobz1GQUxTRX0KICAgIG9wdGlvbnMocmVwci5wbG90LndpZHRoPTEyLCByZXByLnBsb3QuaGVpZ2h0PTQpCiAgICAKICAgIHAxIDwtIERpbVBsb3QobWdzYywgcmVkdWN0aW9uID0gInRzbmUiLCBwdC5zaXplID0gMC4xKSArIGdndGl0bGUobGFiZWwgPSAidC1TTkUiKSAKICAgIHAyIDwtIERpbVBsb3QobWdzYywgcmVkdWN0aW9uID0gInVtYXAiLCBwdC5zaXplID0gMC4xKSArIGdndGl0bGUobGFiZWwgPSAiVU1BUCIpCiAgICBwMyA8LSBEaW1QbG90KG1nc2MsIHJlZHVjdGlvbiA9ICJwY2EiLCBwdC5zaXplID0gMC4xKSArIGdndGl0bGUobGFiZWwgPSAiUENBIikKICAgIAogICAgcDEgPC0gQXVnbWVudFBsb3QocGxvdCA9IHAxICkKICAgIHAyIDwtIEF1Z21lbnRQbG90KHBsb3QgPSBwMiApCiAgICBwMyA8LSBBdWdtZW50UGxvdChwbG90ID0gcDMgKQogICAgKHAxICsgcDIgKyBwMykgJiBOb0xlZ2VuZCgpCmBgYAoKCgoKIyBDbHVzdGVyIHRoZSBjZWxscwoKRm9yIG91ciBzdWJzZXQgb2YgdGltZSBwb2ludCBFNi41LCB3ZSBoYXZlIHRoZSBjZWxsIHR5cGUgYW5ub3RhdGlvbiBpbmZvcm1hdGlvbi4gQWx0aG91Z2ggdGhpcyBpcyBub3QgdXN1YWxseSB0aGUgY2FzZSBpbiBtYW55IGNvbXB1dGF0aW9uYWwgcHJvYmxlbXMsIGkuZS4gd2Ugc3RhcnQgd2l0aG91dCBrbm93aW5nIChvciBoYXZpbmcgYSB2ZXJ5IHZhZ3VlIGlkZWEgb2YpIGhvdyBtYW55IGNsdXN0ZXJzIHdlIHdpbGwgZW5kIHVwLiBGb3Igbm93IGxldCdzIHRha2UgYWR2YW50YWdlIG9mIHRoZSBrbm93biBhbm5vdGF0aW9ucy4gCgoqKlExMToqKiBIb3cgbWFueSAkcmVhbCQgY2xhc3NlcyB3ZSBoYXZlPyBSZXByb2R1Y2UgdGhlIGZvbGxvd2luZyBwbG90cyBjb2xvcmVkIGJ5IGNlbGwgdHlwZS4gKCBgSGludDpgIFlvdSBjYW4gbWFrZSB1c2UgYGdyb3VwLmJ5YCBhcmd1bWVudCBpbiBgRGltUGxvdGAgdG8gZXh0cmFjdCBzdG9yZWQgY2x1c3RlciBJRHMuKQoKYGBge3IgcmVhbGNsYXNzZXMsIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiAgICAjIGxlbmd0aCh1bmlxdWUobWdzY0BtZXRhLmRhdGEkY2VsbHR5cGUpKQogICAgCiAgICBvcHRpb25zKHJlcHIucGxvdC53aWR0aD0xNCwgcmVwci5wbG90LmhlaWdodD02KQogICAgcDEgPC1EaW1QbG90KG1nc2MsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAiY2VsbHR5cGUiKStnZ3RpdGxlKCJVTUFQIGNlbGwgdHlwZXMiKSAKICAgIHAyIDwtRGltUGxvdChtZ3NjLCByZWR1Y3Rpb24gPSAidHNuZSIsIGdyb3VwLmJ5ID0gImNlbGx0eXBlIikrZ2d0aXRsZSgidC1TTkUgY2VsbCB0eXBlIikgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKQogICAgcDEgKyBwMgpgYGAKCgojIyBLLW1lYW5zIAoKU2V1cmF0IGRvZXMgbm90IHN1cHBvcnQgSy1tZWFucz8gV2hhdCBhcmUgd2UgZ29pbmcgdG8gZG8gbm93IT8gOikKCioqUTEyOioqIFdoaWNoIGVsZW1lbnRzIGRvIHdlIG5lZWQgdG8gcGVyZm9ybSAkSy1tZWFucyQ/IFRyeSB0byByZXByb2R1Y2UgdGhlIGZvbGxvd2luZyBwbG90ICgkaz0xMCQpLiAoIGBIaW50OmAgTWFrZSB1c2Ugb2YgYEVtYmVkZGluZ3NgIGZ1bmN0aW9uIG9yIHV0aWxpemUgYW4gaW5mb3JtYXRpb24gd2UgcHJldmlvdXNseSB1c2VkIGluIHRoZSBwcmV2aW91cyBleGFtcGxlcy4gWW91IGNhbiBzYXZlIGNsdXN0ZXIgSURzIHRvIGBtZ3NjQG1ldGEuZGF0YWAgZmllbGQuICkKCgpgYGB7ciBrbWVhbnMsIGVjaG89RkFMU0V9CnBjMzAgPC0gRW1iZWRkaW5ncyhvYmplY3QgPSBtZ3NjLCByZWR1Y3Rpb24gPSAicGNhIikgWywgMTozMF0KbWdzY19rbWVhbnMgPC1rbWVhbnMocGMzMCwgIGl0ZXIubWF4ID0gMTAwLCBjZW50ZXJzPTEwKQoKZGltKHBjMzApCiMgYWRkIGNsdXN0ZXJpbmcgcmVzdWx0cyB0byAnb2JqZWN0QG1ldGEuZGF0YSRrbWVhbnMuY2x1c3RlcnMnCm1nc2NAbWV0YS5kYXRhJGttZWFucy5jbHVzdGVycyA8LSBhcy5mYWN0b3IobWdzY19rbWVhbnMkY2x1c3RlcikKCgpwMSA8LSBEaW1QbG90KG1nc2MsIHJlZHVjdGlvbiA9ICJwY2EiLCBncm91cC5ieSA9ICJrbWVhbnMuY2x1c3RlcnMiLCBsYWJlbD1UUlVFKQpwMiA8LSBEaW1QbG90KG1nc2MsIHJlZHVjdGlvbiA9ICJ0c25lIiwgZ3JvdXAuYnkgPSAia21lYW5zLmNsdXN0ZXJzIiwgbGFiZWw9VFJVRSkKcDMgPC0gRGltUGxvdChtZ3NjLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gImttZWFucy5jbHVzdGVycyIsIGxhYmVsPVRSVUUpCnAxICsgcDIgKyBwMwoKYGBgCgojIyMgSG93IG1hbnkgY2x1c3RlcnMgc2hvdWxkIHdlIGNob29zZT8KCkRvIHlvdSByZW1lbWJlciBvdXIgZXhhbXBsZSBmcm9tIHRoZSBsZWN0dXJlPyBXaGljaCAkayQtbWVhbnMgb3V0Y29tZSBkaWQgd2UgbG9vayBhdCB0aGUgdG8gZGVjaWRlIG51bWJlciBvZiBjbHVzdGVycz8gCgoqKlExMzoqKiBQcmludCB0aGUgZm9sbG93aW5nIHBsb3QgdXNpbmcgdGhlIHNhbWUgZW1iZWRkaW5ncyBpbiB0aGUgcHJldmlvdXMgZXhhbXBsZS4gKGBIaW50OmAgTGVjdHVyZSBub3RlczopKSAKCmBgYHtyIGNsdXN0ZXJudW0sIGVjaG89RkFMU0V9CiAgICB3c3NfIDwtIGxpc3QoKSAKICAgIGNsdXMgPC0gc2VxKDQsIDY0LCBieT00KQogICAgCiAgICBmb3IgKGsgaW4gMTpsZW5ndGgoY2x1cykpIHsKICAgICAgICBtX2ttZWFucyA8LSBrbWVhbnMocGMzMCwgaXRlci5tYXggPSAxMDAsIGNlbnRlcnM9Y2x1c1trXSkKICAgICAgICB3c3NfIDwtIGFwcGVuZCh3c3NfLCBtX2ttZWFucyR0b3Qud2l0aGluc3MpCiAgICAgICAgfQogICAgCiAgICB3c3NfZGF0YSA8LSBkYXRhLmZyYW1lKHdzcz11bmxpc3Qod3NzXyksIGs9Y2x1cykKICAgIGdncGxvdCh3c3NfZGF0YSwgYWVzKHg9aywgeT13c3MpKSArIAogICAgICBnZW9tX2xpbmUoYWVzKHk9IHdzcyksIGxpbmV0eXBlPTIpICsgCiAgICAgIHNjYWxlX3hfY29udGludW91cyhicmVha3M9c2VxKGZyb209MCwgdG89NjQsIGJ5PSA0KSkgKwogICAgICB5bGltKDEwMDAwMCwgMjUwMDAwKQpgYGAKCkNhbiB3ZSBkZWNpZGUgdGhlIG51bWJlciBvZiBjbHVzdGVycyBsb29raW5nIGF0IHRoaXMgcGxvdD8KCiMjIEhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nCgpTZXVyYXQgZG9lcyBub3Qgc3VwcG9ydCBoaWVyYXJjaGljYWwgY2x1c3RlcmluZyB0b28/IEJ1dCB3ZSBjYW4gZG8gaXQhISAKCioqUTE0OioqIFdoaWNoIGVsZW1lbnRzIGRvIHdlIG5lZWQgdG8gcGVyZm9ybSBoaWVyYXJjaGljYWwgY2x1c3RlcmluZz8gVHJ5IHRvIHJlcHJvZHVjZSB0aGUgZm9sbG93aW5nIGRlbmRvZ3JhbS4gVXNlIGBFdWNsaWRpYW5gIGFzIGRpc3RhbmNlIG1ldHJpYyBhbmQgYHdhcmQuRDJgIGFzIGxpbmthZ2UgbWV0aG9kLiAKCgpgYGB7ciBoY2x1c3QsIGVjaG89RkFMU0V9Cm1nc2NfZGlzdCA8LSBkaXN0KCBtZ3NjQHJlZHVjdGlvbnNbWyJwY2EiXV1AY2VsbC5lbWJlZGRpbmdzWywxOjMwXSwgbWV0aG9kPSJldWNsaWRlYW4iKQptZ3NjX2hjZSA8LSBoY2x1c3QobWdzY19kaXN0LCBtZXRob2Q9IndhcmQuRDIiKQoKIyBQbG90IHRoZSBvYnRhaW5lZCBkZW5kcm9ncmFtIC0gdGhpcyBtaWdodCB0YWtlIHRpbWUhIQpwbG90KG1nc2NfaGNlLCBjZXggPSAwLjYsIGhhbmcgPSAtMSwgbWFpbj0nbWdzYyBjbHVzdGVyIGRlbmRvZ3JhbScpCgpgYGAKCioqUTE1OioqIEhvdyBhYm91dCB3ZSBwcmludCBkaWZmZXJlbnQgY2x1c3RlcmluZyBvdXRjb21lcyB3aXRoICRrPTEwLCAyMCwgNDAkIHVzaW5nIHQtU05FPyAKCgpgYGB7ciBoY2x1c3RwbG90LCBlY2hvPUZBTFNFfQojZXVjbGlkZWFuIGRpc3RhbmNlCm1nc2NAbWV0YS5kYXRhJGhjZV8xMCA8LSBjdXRyZWUobWdzY19oY2UsayA9IDEwKQptZ3NjQG1ldGEuZGF0YSRoY2VfMTUgPC0gY3V0cmVlKG1nc2NfaGNlLGsgPSAxNSkKbWdzY0BtZXRhLmRhdGEkaGNlXzIwIDwtIGN1dHJlZShtZ3NjX2hjZSxrID0gMjApCgpvcHRpb25zKHJlcHIucGxvdC53aWR0aD0yMSwgcmVwci5wbG90LmhlaWdodD01KQoKcGxvdF9ncmlkKG5jb2wgPSAzLAogIERpbVBsb3QobWdzYywgcmVkdWN0aW9uID0gInRzbmUiLCBncm91cC5ieSA9ICJoY2VfMTAiLCBsYWJlbD1UUlVFKStnZ3RpdGxlKCJrPTEwIiksCiAgRGltUGxvdChtZ3NjLCByZWR1Y3Rpb24gPSAidHNuZSIsIGdyb3VwLmJ5ID0gImhjZV8xNSIsIGxhYmVsPVRSVUUpK2dndGl0bGUoIms9MTUiKSwKICBEaW1QbG90KG1nc2MsIHJlZHVjdGlvbiA9ICJ0c25lIiwgZ3JvdXAuYnkgPSAiaGNlXzIwIiwgbGFiZWw9VFJVRSkrZ2d0aXRsZSgiaz0yMCIpKQpgYGAKCgojIyBHcmFwaC1iYXNlZCBjbHVzdGVyaW5nCgpTZXVyYXR2MyBhZG9wdHMgYSBncmFwaC1iYXNlZCBjbHVzdGVyaW5nIG1ldGhvZG9sb2d5IG11Y2ggc2ltaWxhciB0byBQaGVub0dyYXBoIGFwcHJvYWNoIHdlIGRpc2N1c3NlZCBlYXJsaWVyLgoKYEZpbmROZWlnaGJvcnMgYCBmdW5jdGlvbiBwZXJmb3JtcyB0aGUgZmlyc3Qgc3RlcHM6ICgxKSBidWlsZCBhIGtOTiBncmFwaCB1c2luZyBFdWNsaWRlYW4gZGlzdGFuY2UgaW4gUENBIHNwYWNlIGFuZCAoMikgdXBkYXRlIHRoZSBlZGdlIHdlaWdodHMgYmFzZWQgb24gdGhlIHNoYXJlZCBuZWlnaGJvcnMgKEphY2NhcmQgaW5kZXgpIHRvIGNvbnN0cnVjdCBhIFNoYXJlZCBOZWFyZXN0IE5laWdoYm9yIChTTk4pIGdyYXBoLiAKCk9uY2UgdGhlIGdyYXBoIGlzIGNvbnN0dWN0ZWQsIHdlIGNhbiB1c2UgIGBGaW5kQ2x1c3RlcnMgYCBmdW5jdGlvbiAgdG8gYXBwbHkgYSBjb21tdW5pdHkgZGV0ZWN0aW9uIGFsZ29yaXRobSB0byBpZGVudGlmeSBzdWJncm91cHMuIFJlbWVtYmVyIHRoYXQsIFNldXJhdCB1c2VzIHRoZSBMb3V2YWluIGFsZ29yaXRobSBhcyBhIGRlZmF1bHQgZm9yIHRoaXMgc3RlcC4KCgpgYGB7ciBzbm4sIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9Cm1nc2MgPC0gRmluZE5laWdoYm9ycyhtZ3NjLCAgay5wYXJhbSA9IDIwLCBkaW1zID0gMTozMCwgcmVkdWN0aW9uID0gInBjYSIpCm1nc2MgPC0gRmluZENsdXN0ZXJzKG1nc2MsIHJlc29sdXRpb24gPSAwLjUpCgoKIyBMb29rIGF0IGNsdXN0ZXIgSURzIG9mIHRoZSBmaXJzdCAyMCBjZWxscwpoZWFkKElkZW50cyhtZ3NjKSwgMjApCmBgYAoKYEZpbmRDbHVzdGVycyBgIGhhcyBhIHJlc29sdXRpb24gcGFyYW1ldGVyIHRoYXQgc2V0cyB0aGUgYGdyYW51bGFyaXR5YCBvZiB0aGUgZG93bnN0cmVhbSBjbHVzdGVyaW5nLCB3aXRoIGluY3JlYXNlZCB2YWx1ZXMgbGVhZGluZyB0byBhIGdyZWF0ZXIgbnVtYmVyIG9mIGNsdXN0ZXJzLiAgCgpUaGUgU2V1cmF0IGF1dGhvcnMgc3VnZ2VzdCB0aGF0ICBgZ3JhbnVsYXJpdHkgdmFsdWVzYCBiZXR3ZWVuIDAuNC0xLjIgdXN1YWxseSByZXR1cm4gZ29vZCByZXN1bHRzIGZvciBzYyBkYXRhc2V0cyBvZiBhcm91bmQgM0sgY2VsbHMuIEZvciBsYXJnZXIgZGF0YXNldHMsIG9wdGltYWwgcmVzb2x1dGlvbiBvZnRlbiBpbmNyZWFzZXMgZm9yIGxhcmdlciBkYXRhc2V0cy4gCgoKKipIb21ld29yazoqKiBFeHBsb3JlIGhvdyAgZGlmZmVyZW50IHZhbHVlcyBvZiAkZ3JhbnVsYXJpdHkkICBhZmZlY3QgdGhlIGNsdXN0ZXJpbmcgcmVzdWx0cyBhbmQgcmVwcm9kdWNlIHRoZSBmb2xsb3dpbmcgcGxvdC4gKEhpbnQ6IFVzZSBgSWRlbnRzYCBmdW5jdGlvbiBvZiBTZXVyYXQgdG8gc2F2ZSBjbHVzdGVyIGlkcy4gKQoKCmBgYHtyIHNubnBsb3QsIG1lc3NhZ2U9RkFMU0UsICB3YXJuaW5nPUZBTFNFLCBlY2hvPUZBTFNFfQogIAogIG1nc2NbWyJvcmcuaWRlbnQiXV0gPC0gSWRlbnRzKG9iamVjdCA9IG1nc2MpICMwLjUKICBtZ3NjIDwtIEZpbmRDbHVzdGVycyhtZ3NjLCByZXNvbHV0aW9uID0gMC40KQogIG1nc2NbWyJpZGVudC4wNCJdXSA8LSBJZGVudHMob2JqZWN0ID0gbWdzYykKICBtZ3NjIDwtIEZpbmRDbHVzdGVycyhtZ3NjLCByZXNvbHV0aW9uID0gMC44KQogIG1nc2NbWyJpZGVudC4wOCJdXSA8LSBJZGVudHMob2JqZWN0ID0gbWdzYykKICBtZ3NjIDwtIEZpbmRDbHVzdGVycyhtZ3NjLCByZXNvbHV0aW9uID0gMS4yKQogIG1nc2NbWyJpZGVudC4xMiJdXSA8LSBJZGVudHMob2JqZWN0ID0gbWdzYykKCiAgCiAgcGxvdF9ncmlkKG5yb3c9MiwgbmNvbCA9IDIsCiAgRGltUGxvdChtZ3NjLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gImlkZW50LjA0IiwgbGFiZWw9VFJVRSkrZ2d0aXRsZSgiU05OIHJlcz0wLjQiKSwKICBEaW1QbG90KG1nc2MsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAib3JnLmlkZW50IiwgbGFiZWw9VFJVRSkrZ2d0aXRsZSgiU05OIHJlcz0wLjUsIGRlZmF1bHQiKSwKICBEaW1QbG90KG1nc2MsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAiaWRlbnQuMDgiLCBsYWJlbD1UUlVFKStnZ3RpdGxlKCJTTk4gcmVzPTAuOCIpLAogIERpbVBsb3QobWdzYywgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICJpZGVudC4xMiIsIGxhYmVsPVRSVUUpK2dndGl0bGUoIlNOTiByZXM9MS4yIikKICAKICApCiAgCmBgYAoKCgoKCgoK